<?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 2020/11/5
 * Time: 14:22
 */

namespace app\api\service;

use app\adminapiv3\model\HostLearnConfigModel;

use app\common\exception\CustomException;
use app\common\model\learn_data\HostLearnDataModel;
use app\common\model\learn_data\HostLearnIconModel;
use app\common\service\Host;
use think\Db;


class LearnDataService extends BaseService
{

    public $host_id;
    public $config_process;//规则处理方式
    public $config_have_mac;//是否记录mac地址
    public $clientRules; //下发至客户端的规则
    public $learnUpdateData;//修改数据数组

    /**
     * @name 学习数据处理
     * @param $hostId
     * @param $learnNumber
     * @return bool
     * @author wx
     * @date 2020/11/6 13:09
     */
    public function handle($hostId, $learnNumber)
    {

        $this->host_id = $hostId;
        if ($hostId == 0 || $learnNumber == 0) {
            throw new CustomException('参数错误', '202');
        }

        //修改回调状态
        Db::name('host_learn_config')->where('host_id', $hostId)->update(['callback_status' => 1, 'learn_status' => 0]);

        //查询此次学习数据
        $hostLearnDataModel = new HostLearnDataModel();
        $hostLearnDataModel->setHostId($hostId);

        $nowLearnData = $hostLearnDataModel
            ->where('host_id', $hostId)
            ->where('learn_number', $learnNumber)
            ->column('src_ip, src_mac, dest_ip, dest_mac, dest_port, direction, protocols, view', 'id');

        //处理学习数据关系
        $this->handleRelation($nowLearnData);

        //标记学习数据
        $this->handleLearnIcon($hostId, array_keys($nowLearnData));

        //处理学习规则
        $this->handleRule($nowLearnData);

        //下发规则给客户端
        $this->addFidAndsendClient();
    }

    /**
     * @name 处理学习数据标识
     * @param $hostId
     * @param $nowLearnData
     * @author wx
     * @date 2020/11/6 14:12
     */
    private function handleLearnIcon($hostId, $nowLearnData)
    {
        //1. 获取缓存数据
        $hostLearnConfigModel = new HostLearnConfigModel();
        $lastLearnDatas = $hostLearnConfigModel->getLearnIdCache($hostId);

        if (empty($lastLearnDatas)) {
            return true;
        }

        $lastLearnData = $lastLearnDatas['ids'];

        //3. 处理数据
        $insertArray = array();
        $isAdd = array_diff($nowLearnData, $lastLearnData);
        $isModify = array_intersect($lastLearnData, $nowLearnData);
        $isDelete = array_diff($lastLearnData, $nowLearnData);

        foreach ($isAdd as $addId) {
            $addTmp['learn_id'] = $addId;
            $addTmp['host_id'] = $hostId;
            $addTmp['is_add'] = 1;
            $addTmp['is_modify'] = 0;
            $addTmp['is_delete'] = 0;

            $insertArray[] = $addTmp;
        }

        foreach ($isModify as $modifyId) {
            $modifyTmp['learn_id'] = $modifyId;
            $modifyTmp['host_id'] = $hostId;
            $modifyTmp['is_add'] = 0;
            $modifyTmp['is_modify'] = 1;
            $modifyTmp['is_delete'] = 0;

            $insertArray[] = $modifyTmp;
        }

        foreach ($isDelete as $deleteId) {
            $deleteTmp['learn_id'] = $deleteId;
            $deleteTmp['host_id'] = $hostId;
            $deleteTmp['is_add'] = 0;
            $deleteTmp['is_modify'] = 0;
            $deleteTmp['is_delete'] = 1;

            $insertArray[] = $deleteTmp;
        }

        //4.1 清空当前主机数据
        $hostLearnIconModel = new HostLearnIconModel();
        $hostLearnIconModel->setHostId($this->host_id);

        $hostLearnIconModel->where('host_id', $hostId)->delete();
        $hostLearnIconModel->limit(1000)->insertAll($insertArray);
    }

    /**
     * @name 处理学习关系
     * @author wx
     * @date 2020/11/6 13:12
     */
    protected function handleRelation($learnData)
    {
        foreach ($learnData as $k => $v) {
            $srcIpInt = ip2long($v['src_ip']);
            $destIpInt = ip2long($v['dest_ip']);

            if ($srcIpInt > $destIpInt) {//src_ip大的情况


                $srcBigWhere = [
                    'ip_small' => $v['dest_ip'],
                    'mac_small' => $v['dest_mac'],
                    'ip_big' => $v['src_ip'],
                    'mac_big' => $v['src_mac'],
                ];


                $info = $this->findRelationData($srcBigWhere);

                if ($info) {//修改原有数据

                    $this->modifyRelationData($info, 'port_small', $v);

                } else {//新加数据

                    $this->addRelationData($v, 'src_big');

                }

            } else {//dest_ip大的情况

                    $destBigWhere = [
                        'ip_small' => $v['src_ip'],
                        'mac_small' => $v['src_mac'],
                        'ip_big' => $v['dest_ip'],
                        'mac_big' => $v['dest_mac'],
                    ];

                $info = $this->findRelationData($destBigWhere);

                if ($info) {//修改原有数据

                    $this->modifyRelationData($info, 'port_big', $v);

                } else {//新加数据

                    $this->addRelationData($v, 'dest_big');
                }
            }

        }
    }

    /**
     * @name 处理规则
     * @author wx
     * @date 2020/11/16 10:13
     */
    public function handleRule($dataRule)
    {
        // auto 1手动下发 2自动下发
        $hostConfig = Db::name('host_learn_config')->where('host_id', $this->host_id)->find();

        $this->config_process = $hostConfig['allow'];//规则处理方式 1允许 0拒绝
        $this->config_have_mac = $hostConfig['have_mac_addr'];

        foreach ($dataRule as $k => $v) {
            if ($hostConfig['auto'] == 2) {//auto 1手动下发, 2自动下发
                //处理规则
                $this->netFilterRule($v);
            }

            //修改学习数据
            $this->learnUpdateData['last_view'] = Db::raw('view');

            $hostLearnDataModel = new HostLearnDataModel();
            $hostLearnDataModel->setHostId($this->host_id);
            $hostLearnDataModel->where('id', $v['id'])->update($this->learnUpdateData);
        }
    }

    /**
     * @name 下发规则至客户端
     * @author wx
     * @date 2020/11/16 13:34
     */
    public function addFidAndsendClient()
    {

        if (empty($this->clientRules))
        {
            return false;
        }

        $commonBase = new \app\common\service\BaseService();
        global $errCode;
        $errCode = $commonBase->CODE_SUCCESS;

        global $online_host;
        $Host = new Host();
        $online_host = $Host->hostList([$this->host_id], 'id,name,os,ip,alias,address_id');

        //改变rule的fid //type 1 手动 2自动
        Db::name('HostRule')
            ->where('host_id', $this->host_id)
            ->where('type', 2)
            ->where('fid', 0)
            ->update(['fid' => Db::raw('`id`')]);

        file_put_contents('../runtime/autoRule.log', date('Y-m-d H:i:s').'学习完成发送下发规则指令'.PHP_EOL, FILE_APPEND);

        //发送下载规则指令
        $commonBase->sendCommandToClient('netfilter', 'downloadRules',true, true);
    }

    /**
     * @name: 处理规则
     * @Param:
     * @Description:
     * @Author: foo
     * @Date: 2019-12-16 15:59:29
     */
    public function netFilterRule ($rule)
    {
        //1. 添加规则
        $srcIpInt = ip2long($rule['src_ip']);
        $destIpInt = ip2long($rule['dest_ip']);
        $destPort = $rule['dest_port'];
        $macAddr = $rule['direction'] == 1 ? $rule['dest_mac'] : $rule['src_mac'];//根据方向判断 1是进栈->目标Mac 2出栈->源Mac


        //force 0不强制下发 1强制下发
        $count = Db::name('HostRule')
            ->where($srcIpInt . ' BETWEEN  src_ip_s AND src_ip_e')
            ->where($destIpInt . ' BETWEEN dest_ip_s AND dest_ip_e')
            ->where($destPort . ' BETWEEN dest_port_s AND dest_port_e')
            ->where('protocols', $rule['protocols'])
            ->where('host_id', $this->host_id)
            ->where('force', 0)
            ->count();

        if ($count == 0)
        {
            $insert = [
                'host_id' => $this->host_id,
                'alias' => 'AutoCreate'. date('Ymd') . $rule['id'] ,
                'direction' => $rule['direction'],
                'protocols' => $rule['protocols'],
                'process' => $this->config_process,//规则处理方式
                'status' => 1,
                'src_port_s' => 0,
                'src_port_e' => 65535,
                'dest_port_s' => $destPort,
                'dest_port_e' => $destPort,
                'src_ip_s' => $srcIpInt,
                'src_ip_e' => $srcIpInt,
                'dest_ip_s' => $destIpInt,
                'dest_ip_e' => $destIpInt,
                'type' => 2,
                'learn_id' => $rule['id'],
                'have_mac_addr' => $this->config_have_mac,
                'mac_addr' => $macAddr
            ];

            //添加到host_rule数据表
            Db::name('HostRule')->insert($insert);

            //2. 构建下发到客户端的规则
            $this->clientRules[] = [
                'alias' => 'AutoCreate'. date('Ymd') . $rule['id'] ,
                'direction' => $rule['direction'],
                'protocols' => $rule['protocols'],
                'process' => $this->config_process,
                'src_port' => 'any',
                'dest_port' => $rule['dest_port'],
                'src_ip' => $rule['src_ip'],
                'dest_ip' => $rule['dest_ip'],
                'mac_addr' => $macAddr,
                'status' => 1,
                'have_mac_addr' => $this->config_have_mac,
            ];

            $this->learnUpdateData['status'] = 1;
        }
    }


    /**
     * @name 查询关系数据
     * @param $condition
     * @return array|null|\PDOStatement|string|\think\Model
     * @author wx
     * @date 2020/11/6 15:14
     */
    private function findRelationData($condition)
    {
        return Db::name('host_learn_relation')->where($condition)->find();
    }

    /**
     * @name 修改学习关系数据
     * @param $field
     * @author wx
     * @date 2020/11/6 13:47
     */
    private function modifyRelationData($info, $field, $v)
    {
        $portArr = json_decode($info[$field], true);

        $portArr = (array) $portArr;

        $isNeedAdd = 1;
        foreach ($portArr as $key => $value){
            if ($v['dest_port'] == $value['port']) {
                $portArr[$key]['view'] = $v['view'];
                $isNeedAdd = 0;
                continue;
            }
        }

        if($isNeedAdd == 1){
            $portTmp['port'] = $v['dest_port'];
            $portTmp['view'] = $v['view'];
            array_push($portArr, $portTmp);
        }

        $updateData[$field] = json_encode($portArr);

        if($field == 'port_big'){
            $updateData['small_to_big'] = 1;
        }else{
            $updateData['big_to_small'] = 1;
        }
        Db::name('host_learn_relation')->where('id',$info['id'])->update($updateData);
    }


    /**
     * @name 处理添加数据
     * @param $v
     * @param $type
     * @author wx
     * @date 2020/11/6 13:57
     */
    private function addRelationData($v, $type)
    {
        $srcHostId = Db::name('host_contact')
            ->where('ip','like', '%'.$v['src_ip'].'%')
            ->where('mac', $v['src_mac'])
            ->value('host_id');

        $destHostId = Db::name('host_contact')
            ->where('ip','like', '%'.$v['dest_ip'].'%')
            ->where('mac', $v['dest_mac'])
            ->value('host_id');

        if($srcHostId && $destHostId){

            if($type == 'src_big'){//src_ip大
                $tmp['ip_small'] = $v['dest_ip'];
                $tmp['mac_small'] = $v['dest_mac'];
                $tmp['host_id_small'] = $srcHostId;
                $tmp['port_small'] = json_encode([['port' => $v['dest_port'], 'view' => $v['view']]]);
                $tmp['ip_big'] = $v['src_ip'];
                $tmp['mac_big'] = $v['src_mac'];
                $tmp['host_id_big'] = $destHostId;
                $tmp['port_big'] = '';
                $tmp['small_to_big'] = 0;
                $tmp['big_to_small'] = 1;

            }else{
                $tmp['ip_small'] = $v['src_ip'];
                $tmp['mac_small'] = $v['src_mac'];
                $tmp['host_id_small'] = $srcHostId;
                $tmp['port_small'] = '';
                $tmp['ip_big'] = $v['dest_ip'];
                $tmp['mac_big'] = $v['dest_mac'];
                $tmp['host_id_big'] = $destHostId;
                $tmp['port_big'] = json_encode([['port' => $v['dest_port'], 'view' => $v['view']]]);
                $tmp['small_to_big'] = 1;
                $tmp['big_to_small'] = 0;
            }

            Db::name('host_learn_relation')->insert($tmp);
        }

    }


}